//
// Copyright 2020 Bloomberg Finance L.P.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//

import Foundation

final class HTMLRenderer: Renderer {
    private let output: AnyOutput<String>
    private let header = """
    <!doctype html>
    <html lang="en">
    <head>
        <meta charset="utf-8">
        <title>xcdiff results</title>
        <meta name="description" content="xcdiff results">
        <meta name="author" content="xcdiff">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <style>
            html, body {
                margin: 0;
                padding: 0;
            }

            body {
                font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
                font-size: 14px;
                line-height: 1.5;
                background-color: #fff;
                color: #333;
            }

            footer {
                border-top: 1px #aaa dotted;
                margin-top: 3em;
                color: #aaa;
                font-size: 11px;
                padding-top: 0.4em;
                padding-bottom: 0.4em;
            }

            section {
                margin-top: 1em;
                padding: 0.1em 1em 0.1em 1em;
                border-radius: 0.7em;
            }

            ul {
                margin-top: 0;
                padding-left: 15px;
            }

            li {
                font-family: 'Courier New', Courier, monospace;
            }

            h1 {
                padding: 0.2em 0.5em 0.2em 0.5em;
                font-size: 24px;
                font-weight: 300;
            }

            h2 {
                padding: 0.2em 0.5em 0.2em 0.5em;
                border-radius: 0.2em;
                font-size: 14px;
                font-family: 'Courier New', Courier, monospace;
            }

            h3 {
                font-size: 12px;
                padding-left: 0.2em;
            }

            p {
                margin: 0;
            }

            .container {
                max-width: 1200px;
                padding: 10px;
                margin-right: auto;
                margin-left: auto;
            }

            .warning {
                background-color: #fffce3;
            }

            .warning h2 {
                background-color: #fff2b8;
            }

            .success {
                background-color: #f1ffe9;
            }

            .content {
                padding-left: 2em;
            }
        </style>
    </head>
    <body>
        <div class="container">
            <header>
                <h1>Δ xcdiff result</h1>
            </header>
    """

    private let footer = """
            <footer>
                Generated by <a href="https://github.com/bloomberg/xcdiff">xcdiff</a>.
            </footer>
        </div>
    </body>
    </html>
    """

    init(output: AnyOutput<String>) {
        self.output = output
    }

    func begin() {
        output.write(header)
    }

    func end() {
        output.write(footer)
    }

    func section(_ style: RendererElement.Style, _ content: () -> Void) {
        let cssClass = self.cssClass(from: style)
        switch cssClass {
        case .success, .warning:
            tag("section", cssClass, content)
        case .content:
            tag("div", cssClass, content)
        }
    }

    func header(_ text: String, _ header: RendererElement.Header) {
        tag(tag(from: header), nil, text)
    }

    func text(_ text: String) {
        tag("p", nil, text.htmlEscaped())
    }

    func pre(_ text: String) {
        self.text(text.htmlEscaped())
    }

    func list(_ content: () -> Void) {
        tag("ul", nil, content)
    }

    func item(_ text: String) {
        item {
            output.write(text.htmlEscaped())
        }
    }

    func item(_ content: () -> Void) {
        tag("li", nil, content)
    }

    func line(_: Int) {
        // not needed
    }

    // MARK: - Private

    private func tag(_ name: String, _ cssClass: CSSClass? = nil, _ content: String) {
        tag(name, cssClass) {
            output.write(content)
        }
    }

    private func tag(_ name: String, _ cssClass: CSSClass? = nil, _ content: () -> Void) {
        let cssClassString: String
        if let cssClass {
            cssClassString = " class=\"\(cssClass.rawValue)\""
        } else {
            cssClassString = ""
        }
        output.write("""
        <\(name)\(cssClassString)>
        """)
        content()
        output.write("""
        </\(name)>
        """)
    }

    private enum CSSClass: String {
        case success
        case warning
        case content
    }

    private func tag(from header: RendererElement.Header) -> String {
        switch header {
        case .h1:
            return "h1"
        case .h2:
            return "h2"
        case .h3:
            return "h3"
        }
    }

    private func cssClass(from style: RendererElement.Style) -> CSSClass {
        switch style {
        case .content:
            return .content
        case .success:
            return .success
        case .warning:
            return .warning
        }
    }
}
